package mult.test;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.os.Bundle;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class MultTestActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new MyView(this));
    }
    
    class MyView extends View{
    	
    	//멀티터치 판별 플래그.
    	boolean isMult = false;
    	boolean postMult = false;
    	//스케일 멀티무브 최초값 플래그.
    	boolean firstScale = false;
    	
    	//최초좌표.
    	float firstX;
    	float firstY;
    	
    	//각도값.
    	float preAngle;
    	float Angle;
    	float postAngle;
    	float saveAngle = 0;
    	
    	//거리값.
    	float preDistance;
    	float postDistance;
    	
    	//축척값.
    	float preScale;
    	float Scale = 1;
    	float postScale = 1;
    	float saveScale = 1; 
    	
    	//센터값.
    	float centerX;
    	float centerY;
    	
    	//이동값.
    	float postDX;
    	float postDY;
    	float saveDX;
    	float saveDY;
    	
    	Bitmap bitmap;
    	Paint paint;
    	
    	//종류별메트릭스.
    	Matrix matrix;
    	Matrix matrixT;
    	Matrix matrixRS;

		public MyView(Context context) {
			super(context);
			bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon);
			paint = new Paint();
			
			//메트릭스 셋팅.
			matrix = new Matrix(); //종합메트릭스.
			matrixT = new Matrix(); //이동메트릭스.
			matrixRS = new Matrix(); //회전메트릭스.
			
			
			//최초 센터값. 이후로도 계속 추적해야함.
			centerX = bitmap.getWidth()/2;
			centerY = bitmap.getHeight()/2;
			
			Log.d("june", "최초 센터점 : " + centerX + ", " + centerY);
			
			
		}
		
		@Override
		protected void onDraw(Canvas canvas) {
			// TODO Auto-generated method stub
			super.onDraw(canvas);
			
			canvas.drawBitmap(bitmap, matrix, paint);
			paint.setColor(Color.RED);
			canvas.drawCircle(centerX, centerY, 5, paint);
		}
		
		@Override
		public boolean onTouchEvent(MotionEvent event) {
			// TODO Auto-generated method stub
			
			//이벤트 원터치 좌표.
			float x = event.getX();
			float y = event.getY();
			
			switch (event.getAction()) {
			
			case MotionEvent.ACTION_DOWN:
				//멀티터치플래그 해제.
				isMult = false;
				postMult = false;
				
				//최초 터치값 보존.
				firstX = x;
				firstY = y;
				
				Log.d("june", "싱글터치다운");
				break;
				
				
			case MotionEvent.ACTION_POINTER_1_DOWN:
				//이놈은 죽어도 안찍히네... 왜있는거지?
				isMult = true;
				Log.d("june", "포인트 원 터치다운");
				break;
				
				
			case MotionEvent.ACTION_POINTER_2_DOWN:
				
				//센터값 보정.
				centerX = saveDX + bitmap.getWidth()/2;
				centerY = saveDY + bitmap.getHeight()/2;
				
				//멀티터치시 최초각도.
				//이게문제인듯해. 다운시의 각도와 현재각도를 비교해야하니까...
				//이건 단지 최초좌표일뿐 현재각도가 아니잖아.
				preAngle = getDegreeFromCartesian(event);
				
				//여기서 최초 거리도 나와야될듯해.
				preDistance = spacing(event);
				
				isMult = true;
				firstScale = true;
				
				//여기서 뭐가있길래 센터값이 바뀔까.
				
				Log.d("june", "포인트 투 터치다운, 멀티터치 최초각도 : " + preAngle + ", 투 다운시 스케일값 : " + saveScale);
				break;
				
				 
			//어쨌든 아래의 업 이벤트들중 하나라도 동작하면 멀티터치는 아니란 소리.
			//그러니까 3개 모두 멀티터치 false를 주자. 이러면 꼬이지는 않을거다.
			//결국 모든 흐름의 종착지는 여기. 어떻게되도 싱글업으로 통한다.
			case MotionEvent.ACTION_UP:
				
				if(!postMult){
					Log.d("june", "멀티터치 없는 싱글터치업");
					
					//싱글터치업시 마지막 posetD 값을 저장.
					saveDX = postDX;
					saveDY = postDY;
					
					//센터값의 갱신. 여기가 문제일까...? 근데 싱글터치 없을때잖아
					//반응은 투터치에서 일어나는데...
					centerX = saveDX + bitmap.getWidth()/2;
					centerY = saveDY + bitmap.getHeight()/2;
					
				} else {
					Log.d("june", "멀티터치 이후 싱글터치업");
					
					//세이브값. 원래의 각도를 가지고 계산하기위해 필요하다.
					saveAngle = postAngle;
					saveScale = postScale;
					
					firstScale = false;
				}
				
				invalidate();
				
				break;
				
				
			case MotionEvent.ACTION_POINTER_1_UP:
				//먼저 찍힌 포인트가 먼저 떨어졌을때 호출된다.
				Log.d("june", "포인트 원 터치업");
				isMult = false;
				postMult = true;
				break;
				
				
			case MotionEvent.ACTION_POINTER_2_UP:
				//나중에 찍힌 포인트가 먼저 떨어질때 호출된다. 
				//...차이가 뭐냐 ㅅㅂ...
				Log.d("june", "포인트 투 터치업");
				isMult = false;
				postMult = true;
				break;
				
				
			case MotionEvent.ACTION_MOVE:
				
				//멀티터치가 맞을떄.
				if(isMult){
					
					
					//각도 계산 시작.
					//약간의 이동만으로도 각도가 엄청 바뀌네...?
					//리셋문제가 아니라 계산식의 문제.
					Angle = getDegreeFromCartesian(event);
					
					postAngle = saveAngle - (preAngle - Angle);
					//각도 한계값. 무한히 커지거나 무한히 작아지는걸 방지.
					if(postAngle > 360){
						postAngle = 0;
					} else if(postAngle < 0){
						postAngle = 360;
					}
					//각도계산 종료.
					
					
					
					
					//스케일 처리공정 시작. 
					postDistance = spacing(event);
					
					//신규값. 이전값에다가 신규값의 변동사항을 적용시켜야겠지?
					//아 이놈은 멀티무브중에 딱 한번만 실행되야하네.
					if(firstScale){
						preScale = postDistance / preDistance;
						Log.e("june", "멀티무브 최초 스케일값. : " + preScale);
						firstScale = false;
					}
					
					Scale = postDistance / preDistance;
					postScale = saveScale - (preScale-Scale);
					
					//스케일 한계값.
					if(postScale > 2){
						postScale = 2f;
					} else if(postScale < 0.5){
						postScale = 0.5f;
					}
					//스케일 처리공정 종료.
					

					//스케일도 중심좌표를 기준으로 늘어나잖아? 근데 왜 중점좌표가 틀어지지?
					//가설1. 로테이트시에 회전이후 센터값이 변경된다.
					//가설2. 스케일 조정시 센터값이 변경된다.
					//가설3. 전혀 엉뚱한...?
					//확인. 투터치다운에서 센터값을 보정해주니까 해결됬다.
					
					 
					
					//두개의 매트릭스를 하나로 묶어서...
					matrix.reset();
					matrixRS.reset();
					matrixRS.setRotate(postAngle, centerX, centerY);
					matrixRS.postScale(postScale, postScale, centerX, centerY);
					matrixT.setTranslate(saveDX, saveDY);
					matrix.setConcat(matrixRS, matrixT);
					//메트릭스처리 종료.
					
					
					Log.d("june", "멀티터치무브라우. 멀티터치 동작중 각도 : " + Angle + ", 계산이후 saveAngle : " + saveAngle +
							", 계산이후 scale값 : " + saveScale);
					
				}
				
				//싱글터치일때.
				else {
					if(!postMult){
						
						//현재의 갭. 
						float dx = firstX - event.getX();
						float dy = firstY - event.getY();
						
						//저장되어있던 갭값과의 차이.
						//한번 더 무브가 동작하면 - 값이 뜨고있다. 
						//좌표가 뒤집히는 이유는? 아마 현배의 갭값때문일듯
						postDX = saveDX - dx;
						postDY = saveDY - dy;
						
						//좌표 뒤집힘은 센터값을 다시 설정해줌으로서 해결됬다.
						//그런데 이게 뭔 의미가 있는거지?
						//로테이트시의 센터값... 업 이벤트가 발생하기 이전에 처리해야하는걸까.
						centerX = postDX + bitmap.getWidth()/2;
						centerY = postDY + bitmap.getHeight()/2;
						
						//메트릭스 셋팅.
						//해당 처리이후 좌표가 로테이트값으로 뒤집히는 현상 발생.
						//뭘까 이건. 또 강제로 포인트회전을 시켜야할까...?
						//센터포인트는 제대로 이동중.
						matrix.reset();
						matrixT.reset();
						matrixRS.reset();
						matrixRS.setRotate(saveAngle, centerX, centerY);
						matrixRS.postScale(postScale, postScale, centerX, centerY);
						matrixT.setTranslate(postDX, postDY);
						matrix.setConcat(matrixRS, matrixT);
						
						Log.d("june", "싱글터치무브랩니다. postD값 : " + postDX + ", " + postDY + ", saveAngle값 : " + saveAngle);
						
					}
					
				}
				
				invalidate();
				
				break;
			}
			
			return true;
		}
    	
    }
    
    /**
	 * 터치이벤트에서 이벤트만 따서 싱글터치와 멀티터치간의 거리를 측정해서 반환한다.
	 * @param event 터치이벤트의 이벤트. 주의할건 그냥 액션다운에서는 1번좌표를 인식하지 못한다는것이다.
	 * 				이를 위해서 액션 포인트 다운에서 측정해야한다.(있는줄도 몰랐다...)
	 * 				액션 포인트 다운은 두번째 터치가 이루어졌을때의 다운이벤트이다.
	 * @return 첫좌표와 두번째 좌표의 거리.
	 */
	private float spacing(MotionEvent event) {
	    float x = event.getX(0) - event.getX(1);
	    float y = event.getY(0) - event.getY(0);
	    Log.d("distans return ", "distans return : " + FloatMath.sqrt(x * x + y * y)); 
	    return FloatMath.sqrt(x * x + y * y);
	   
	}
    
    /**
	 * @param event 아예 이벤트를 받아서 처리하는게 간편하겠죠.
	 * @return 이걸로 두 점을 이은 실선의 각도가 구해졌습니다. 짠!
	 */
	private float getDegreeFromCartesian(MotionEvent event)
	{
		
		float angle = (float) Math.atan2((event.getX(1) - event.getX(0)), (event.getY(1)-event.getY(0)));
		float angleindegree = (float) (angle * 180/Math.PI);
		
		return 180-angleindegree;
		
	}
	
	/**
	 * 회전변환식. 참조했던 수식이 망나니수식. 수학책을 뒤적거려서 짜냈다. 결과는 제대로 반환된다.
	 * 회전각의 중심좌표를 파란점으로 표기한다.
	 * @param rotatePX 회전할 포인트.
	 * @param rotatePY 회전할 포인트.
	 * @param oriX 기준 좌표.
	 * @param oriY 기준 좌표.
	 * @param radian 회전할 각도.
	 * @return
	 */
	public Point rotatePoint(float rotatePX, float rotatePY, float oriX, float oriY, float radian){
		
		Log.e("june", oriX + ", " + oriY + ", " + rotatePX + ", " + rotatePY + ", " + radian);
		
		//중심좌표.
		Paint paint = new Paint();
		paint.setColor(Color.BLUE);
		
		//360도 차익. 설정된 회전각을 반시계방향으로 돌린다. 시계방향으로 돌려야한다면 아래줄에 주석처리.
		radian = (360-radian);
		
		float r = (float) (radian*Math.PI/180);
		
		float postRPX = (float) (((rotatePX-oriX)*Math.cos(r) - (rotatePY-oriY)*Math.sin(r))+oriX);
		float postRPY = (float) (((rotatePX-oriX)*Math.sin(r) + (rotatePY-oriY)*Math.cos(r))+oriY);
		
		Point ReturnPoint = new Point((int)postRPX, (int)postRPY); 
		
        return ReturnPoint;
	}
	
	
	
}